import 'dart:async';
import 'dart:math';

import 'package:star_author/utils/common_util/log_util.dart';
import 'package:star_author/utils/http/http_util.dart';
import 'package:star_author/views/products/bidder_goods_detail_page/bidder_goods_detaill_page.dart';
import 'package:star_author/widgets/custom_dialog/custom_alert_dialog.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:mobile_scanner/mobile_scanner.dart';

import '../config/ui_constant.dart';
import '../handler/common_permission_handler.dart';

class QRCodeUtil {
  static void show(
    context, {
    String entrance = QRCodeDialog.bidderDetail,
    callback,
    CameraFacing fact = CameraFacing.back,
  }) {
    FocusScope.of(context).unfocus();
    CommonPermissionHandler.cameraRequestPermission(context, () {
      Get.to(
        QRCodeDialog(
          entrance: entrance,
          fact: fact,
        ),
        preventDuplicates: false,
      )?.then((value) {
        if (value != null) {
          if (callback != null) {
            callback(value);
          }
        }
      });
    });
  }
}

//todo:扫码组件
class QRCodeDialog extends StatefulWidget {
  static const String mallDetail = 'mallDetail';
  static const String bidderDetail = 'bidderDetail';
  static const String logistic = 'logistic'; //物流单号扫码
  static const String stream = 'stream'; //直播间扫码
  final CameraFacing fact;

  const QRCodeDialog({
    Key? key,
    this.entrance = QRCodeDialog.bidderDetail,
    this.fact: CameraFacing.back,
  }) : super(key: key);

  final String entrance;

  @override
  State<QRCodeDialog> createState() => _QRCodeDialogState();
}

class _QRCodeDialogState extends State<QRCodeDialog> {
  late MobileScannerController controller;

  final GlobalKey qrKey = GlobalKey(debugLabel: 'QR');

  @override
  void initState() {
    controller = MobileScannerController(
      torchEnabled: false,
      facing: widget.fact,
    );
    super.initState();
  }

  @override
  void dispose() {
    controller.stop();
    controller.dispose();
    super.dispose();
  }

  @override
  void reassemble() {
    super.reassemble();
    controller.stop();
  }

  @override
  Widget build(BuildContext context) {
    var scanArea = (MediaQuery.of(context).size.width < 360 ||
            MediaQuery.of(context).size.height < 360)
        ? 200.0
        : 260.0;
    return Scaffold(
        appBar: AppBar(
          centerTitle: true,
          backgroundColor: Colors.black,
          title: Text(
            "二维码/条形码",
            style: TextStyle(fontSize: 18.sp, color: Colors.white),
          ),
        ),
        body: Stack(
          children: [
            _buildQrView(context),
            Center(
                child: Container(
              decoration: ShapeDecoration(
                shape: QrScannerOverlayShape(
                  borderColor: UIConstant.colorE02020,
                  borderRadius: 5,
                  borderLength: 20,
                  borderWidth: 5,
                  cutOutSize: scanArea,
                ),
              ),
            )),
          ],
        ));
  }

  Widget _buildQrView(BuildContext context) {
    return MobileScanner(
      allowDuplicates: true,
      key: qrKey,
      controller: controller,
      onDetect: _onQRViewCreated,
    );
  }

  /// 节流
  ///
  /// [func]: 要执行的方法
  /// [delay]: 要迟延的时长
  void throttle(
    Function func, [
    Duration delay = const Duration(milliseconds: 2000),
  ]) {
    Timer? timer;
    if (timer != null) return;
    timer = Timer(delay, () {
      func.call();
    });
  }

  void _onQRViewCreated(Barcode barcode, MobileScannerArguments? args) {
    if (status) {
      LogUtil.v("object-$status");
      stop();
      if (barcode.rawValue?.isNotEmpty ?? false) {
        onSearchIdBySerial(barcode.rawValue);
      }
    }
  }

  bool status = true;

  void stop() {
    this.controller.stop();
    status = false;
  }

  void start() {
    status = true;
    this.controller.start();
  }

  void onSearchIdBySerial(String? code) {
    //物流扫码获取到数据直接返回页面填充
    if ([QRCodeDialog.logistic, QRCodeDialog.stream]
        .contains(widget.entrance)) {
      Get.back(result: code);
      return;
    }
    bool isMall = false;
    if (widget.entrance == QRCodeDialog.bidderDetail) {
      isMall = false;
    } else if (widget.entrance == QRCodeDialog.mallDetail) {
      isMall = true;
    }
    HttpUtil.commonRequest(
      Get.context!,
      '/api/v1/backflows/search_id_by_serial',
      queryParameters: {"serial": code, "mall": isMall},
      isErrorToast: false,
    ).then((value) {
      LogUtil.v("value.data:${value.data}");
      if (value.data != null) {
        Widget detailPage = BidderGoodsDetailPage(id: value.data['id']);

        if (widget.entrance == QRCodeDialog.bidderDetail) {
          detailPage = BidderGoodsDetailPage(id: value.data['id']);
        }

        Get.to(detailPage)!.then((value) {
          start();
        });
      }
    }).catchError((e) {
      DioError dioError = e as DioError;

      showDialog(
        context: context,
        barrierDismissible: false,
        builder: (dialogContext) {
          return CustomAlertDialog(
            title: "扫描失败",
            content: "${dioError.response?.data['msg'] ?? ""}，重新扫描",
            isShowCancelButton: false,
            confirmBtnStr: "重新扫描",
            confirmCallback: () {
              start();
            },
          );
        },
      );
    });
  }
}

class QrScannerOverlayShape extends ShapeBorder {
  QrScannerOverlayShape({
    this.borderColor = Colors.red,
    this.borderWidth = 3.0,
    this.overlayColor = const Color.fromRGBO(0, 0, 0, 80),
    this.borderRadius = 0,
    this.borderLength = 40,
    double? cutOutSize,
    double? cutOutWidth,
    double? cutOutHeight,
    this.cutOutBottomOffset = 0,
  })  : cutOutWidth = cutOutWidth ?? cutOutSize ?? 250,
        cutOutHeight = cutOutHeight ?? cutOutSize ?? 250 {
    assert(
      borderLength <=
          min(this.cutOutWidth, this.cutOutHeight) / 2 + borderWidth * 2,
      "Border can't be larger than ${min(this.cutOutWidth, this.cutOutHeight) / 2 + borderWidth * 2}",
    );
    assert(
        (cutOutWidth == null && cutOutHeight == null) ||
            (cutOutSize == null && cutOutWidth != null && cutOutHeight != null),
        'Use only cutOutWidth and cutOutHeight or only cutOutSize');
  }

  final Color borderColor;
  final double borderWidth;
  final Color overlayColor;
  final double borderRadius;
  final double borderLength;
  final double cutOutWidth;
  final double cutOutHeight;
  final double cutOutBottomOffset;

  @override
  EdgeInsetsGeometry get dimensions => const EdgeInsets.all(10);

  @override
  Path getInnerPath(Rect rect, {TextDirection? textDirection}) {
    return Path()
      ..fillType = PathFillType.evenOdd
      ..addPath(getOuterPath(rect), Offset.zero);
  }

  @override
  Path getOuterPath(Rect rect, {TextDirection? textDirection}) {
    Path _getLeftTopPath(Rect rect) {
      return Path()
        ..moveTo(rect.left, rect.bottom)
        ..lineTo(rect.left, rect.top)
        ..lineTo(rect.right, rect.top);
    }

    return _getLeftTopPath(rect)
      ..lineTo(
        rect.right,
        rect.bottom,
      )
      ..lineTo(
        rect.left,
        rect.bottom,
      )
      ..lineTo(
        rect.left,
        rect.top,
      );
  }

  @override
  void paint(Canvas canvas, Rect rect, {TextDirection? textDirection}) {
    final width = rect.width;
    final borderWidthSize = width / 2;
    final height = rect.height;
    final borderOffset = borderWidth / 2;
    final _borderLength =
        borderLength > min(cutOutHeight, cutOutHeight) / 2 + borderWidth * 2
            ? borderWidthSize / 2
            : borderLength;
    final _cutOutWidth =
        cutOutWidth < width ? cutOutWidth : width - borderOffset;
    final _cutOutHeight =
        cutOutHeight < height ? cutOutHeight : height - borderOffset;

    final backgroundPaint = Paint()
      ..color = overlayColor
      ..style = PaintingStyle.fill;

    final borderPaint = Paint()
      ..color = borderColor
      ..style = PaintingStyle.stroke
      ..strokeWidth = borderWidth;

    final boxPaint = Paint()
      ..color = borderColor
      ..style = PaintingStyle.fill
      ..blendMode = BlendMode.dstOut;

    final cutOutRect = Rect.fromLTWH(
      rect.left + width / 2 - _cutOutWidth / 2 + borderOffset,
      -cutOutBottomOffset +
          rect.top +
          height / 2 -
          _cutOutHeight / 2 +
          borderOffset,
      _cutOutWidth - borderOffset * 2,
      _cutOutHeight - borderOffset * 2,
    );

    canvas
      ..saveLayer(
        rect,
        backgroundPaint,
      )
      ..drawRect(
        rect,
        backgroundPaint,
      )
      // Draw top right corner
      ..drawRRect(
        RRect.fromLTRBAndCorners(
          cutOutRect.right - _borderLength,
          cutOutRect.top,
          cutOutRect.right,
          cutOutRect.top + _borderLength,
          topRight: Radius.circular(borderRadius),
        ),
        borderPaint,
      )
      // Draw top left corner
      ..drawRRect(
        RRect.fromLTRBAndCorners(
          cutOutRect.left,
          cutOutRect.top,
          cutOutRect.left + _borderLength,
          cutOutRect.top + _borderLength,
          topLeft: Radius.circular(borderRadius),
        ),
        borderPaint,
      )
      // Draw bottom right corner
      ..drawRRect(
        RRect.fromLTRBAndCorners(
          cutOutRect.right - _borderLength,
          cutOutRect.bottom - _borderLength,
          cutOutRect.right,
          cutOutRect.bottom,
          bottomRight: Radius.circular(borderRadius),
        ),
        borderPaint,
      )
      // Draw bottom left corner
      ..drawRRect(
        RRect.fromLTRBAndCorners(
          cutOutRect.left,
          cutOutRect.bottom - _borderLength,
          cutOutRect.left + _borderLength,
          cutOutRect.bottom,
          bottomLeft: Radius.circular(borderRadius),
        ),
        borderPaint,
      )
      ..drawRRect(
        RRect.fromRectAndRadius(
          cutOutRect,
          Radius.circular(borderRadius),
        ),
        boxPaint,
      )
      ..restore();
  }

  @override
  ShapeBorder scale(double t) {
    return QrScannerOverlayShape(
      borderColor: borderColor,
      borderWidth: borderWidth,
      overlayColor: overlayColor,
    );
  }
}
